#ifndef NODO_H_
#define NODO_H_

/**********************************************************************
 * nodo.h
 * 
 * Descripcion: Esta clase representa un nodo de un arbol que empaqueta
 * a un bloque. Se debe crear pasandole una ref a un bloque
 * por param y luego utilizar sus metodos para ingresar u obtener
 * datos del nodo en el bloque.
 * El formato del nodo del arbol es:
 * [esp_libre:uint, nivel:ushort, ref_izq:ushort, (clave:TipoDato, dato: TipoDato, ref_der: ushort)+]
 * Si el numero de nivel es 0, no se guardan las ref a nodo.
*********************************************************************/

#include "bloque.h"
#include <climits>

#define TAM_REF sizeof(unsigned short int) //Tamanio de las referencias a nodos
#define PRIMERO 2 * TAM_REF //Arranca desplazado hasta la primera clave

class Nodo {
	
	private:
	
		Bloque* bloque; //Bloque que se esta empaquetando
		TipoDato* clave_base; //Tipo base de la clave
		TipoDato* dato_base; //Tipo base del dato
		unsigned int pos_act; //Pos actual en el bloque
		
	public:
	
		static unsigned short BAD_REF; //Valor estatico q indica mala referencia
		
		/*
		 * Constructor: Recibe el bloque a empaquetar, y los tipos bases
		 * para la clave y el dato. Los tipos no pueden ser NULL.
		 */
		Nodo (Bloque* bloque, TipoDato* clave_base, TipoDato* dato_base);
		
		/*
		 * Constructor sin dato: Idem anterior pero el nodo no tiene datos.
		 * Solo claves y referencias.
		 */
		Nodo (Bloque* bloque, TipoDato* clave_base);
		
		/*
		 * Libera los tipos bases cargados en el nodo.
		 */
		virtual ~Nodo();
		
		/*
		 * Devuelve el nivel del nodo. Si no se ha cargado el nivel
		 * devuelve BAD_REF.
		 */
		unsigned short int get_nivel();
		
		/*
		 * Coloca el nuevo nivel del nodo.
		 */
		void set_nivel(unsigned short int nivel);
		
		/*
		 * Devuelve el espacio libre en el nodo.
		 */
		unsigned int get_esp_libre_nodo();
		
		/*
		 * Posiciona el nodo en su primer elemento.
		 */
		void primero();
		
		/*
		 * Siguiente: Mueve el puntero a elem actual al siguiente elemento.
		 * Si es el ultimo devuelve false, sino devuelve true.
		 */
		bool siguiente();
		
		/*
		 * Anterior: Mueve el puntero a elem actual al elemento anterior.
		 * Si es el primero devuelve false, sino devuelve true.
		 */
		bool anterior();
		
		/*
		 * Posiciona al nodo en su ultimo elemento.
		 */
		void ultimo();
		
		/*
		 * Get Clave: Devuelve la clave que se encuentra en la pos actual del nodo.
		 * LLena el contenido de la clave pasada como param.
		 * Devuelve true si pudo llenar la clave, false en caso contrario.
		 */
		bool get_clave(TipoDato* clave);
		
		/*
		 * Get Dato: Devuelve el dato que es encuentra en la pos actual del nodo.
		 * LLena el contenido del dato pasado como param. Devuelve true.
		 * Si la pos es invalida, el nodo esta vacio o no tiene datos devuelve false. 
		 */
		bool get_dato(TipoDato* dato);
		
		/*
		 * Get Ref Izq: Devuelve la ref izq del elemento en la pos actual. 
		 * Si no hay hijo izq devuelve BAD_REF.
		 */
		unsigned short get_ref_izq();
		
		/*
		 * Get Ref Der: Devuelve la ref der del elemento en la pos actual.
		 * Si no hay hijo der devuelve BAD_REF.
		 */
		unsigned short get_ref_der();
		
		/*
		 * Ingresar elemento: Ingresa un nuevo elemento [clave, dato]
		 * en el lugar que corresponda segun un ordenamiento creciente por clave.
		 * Si el elemento no entra o ya esta en el nodo, 
		 * devuelve false y posiciona el ptero actual en el primer elem. 
		 * Sino devuelve true y deja como pos actual
		 * el elemento recien ingresado.
		 */
		virtual bool ingresar_elemento(TipoDato* clave, TipoDato* dato);
		
		/*
		 * Set Ref Izq: Setea la ref izq del elemento actual.
		 * Si es un nodo hoja no hace nada.
		 */
		void set_ref_izq(unsigned short ref_izq);
		
		/*
		 * Set Ref Der: Setea la ref der del elemento actual.
		 * Si es un nodo hoja no hace nada.
		 */
		void set_ref_der(unsigned short ref_der);
		
		/*
		 * Quitar Elemento: Dada una clave, busca el elemento en
		 * el nodo. Si lo encuentra lo remueve del mismo, haciendo
		 * una compresion, y devuelve true. Sino devuelve false y posiciona
		 * el elemento actual en el primer elem.
		 */
		bool quitar_elemento (TipoDato* clave);
		 
		/*
		 * Buscar Elemento: Dada una clave, busca el elemento en el nodo.
		 * Si lo encuentra devuelve true y posiciona al elemento actual
		 * en el buscado. Sino devuelve false y posiciona al elemento actual
		 * en el lugar donde debería estar la clave (siguiente mayor). Si
		 * la clave debería ser el ultimo elemento, el elem actual queda
		 * apuntando al ultimo (anterior menor).
		 */
		bool buscar_elemento (TipoDato* clave);
		  
		/*
		 * Devuelve true si el nodo esta vacio (no tiene elementos),
		 * false en caso contrario.
		 */
		bool vacio();
		
		/*
		 * Vacia el contenido del nodo, excepto el nivel, y deja la primera
		 * ref izq en bad ref.
		 */
		void vaciar();  
		  
		/*
		 * Es hoja: Devuelve true si el nodo es hoja (nivel = 0), false
		 * en caso contrario.
		 */
		bool es_hoja();
		  
		/*
		 * Devuelve la referencia al bloque contenido en el nodo.
		 */
		Bloque* get_bloque();
		  
		/*
		 * Este metodo sirve para ingresar un dato cuando no entra en
		 * el nodo. Para esto, divide el contenido del this + el elemento
		 * pasado por parametro en 2, agregando la mitad mas alta en el nodo
		 * pasado por parametro y la mas baja en this. 
		 * El elemento del medio lo devuelve en los parametros.
		 * Si el elemento nuevo cayo en el nodo derecho (this):
		 *  Devuelve -1 y posiciona al nodo this en el nuevo elemento y al nuevo en el primero.
		 * Si el elemento nuevo cayo en el nodo izquierdo (nodo nuevo):
		 *  Devuelve 1 y posiciona al nodo this en el ultimo y al nodo nuevo en el nuevo elemento.
		 * Si el elemento nuevo es el del medio, lo devuelve por parametros y:
		 *  Devuelve 0 y posiciona al nodo this en el ultimo y al nodo nuevo en el primero.
		 */
		char dividir_nodo(Nodo * nodo_nuevo, TipoDato ** clave, TipoDato ** dato);
		    
		/*
		 * Devuelve un clon del tipo de clave base
		 */
		TipoDato * get_clave_base();
		     
		/*
		 * Devuelve un clon del tipo de dato base
		 */
		TipoDato * get_dato_base();
		     
		/*
		 * Quita el elemento de la posicion actual. Devuelve true
		 * si estuvo todo bien, false si la posicion actual no era 
		 * valida. Deja posicionado en el siguiente.
		 */
		bool quitar_elemento();
		
		/*
		 * Une los elementos contenidos en this y los del nodo pasado como
		 * parametro en this, si los mismos entran. Sino devuelve false.
		 * Los elementos del nodo pasado como parametro son removidos.
		 */
		bool unir(Nodo * nodo);
			      		
	private:
	
		/*
		 * Devuelve true si la pos_act es valida, false en caso contrario.
		 */
		bool pos_valida();
	
		/*
		 * Devuelve el tamaño de la clave actual, 0 si no se esta apuntando
		 * a ninguno. 
		 */
		unsigned int get_tam_clave_actual();
		
		/*
		 * Devuelve el tamanio del dato actual, 0 si no se esta apuntando
		 * a ninguno.
		 */
		unsigned int get_tam_dato_actual();
		
		/*
		 * Devuelve el tamanio del elemento actual, union de los tamanios
		 * de la clave, el dato y la ref derecha actual.
		 * Devuelve 0 si no es esta apuntando a ninguno.
		 */
		unsigned int get_tam_elem_actual();
		
		/*
		 * Devuelve el tamanio del dato en el bloque que comienza en pos, segun el tipo
		 * de dato pasado como param.
		 */
		unsigned int get_tam_dato(unsigned int pos, CodTipo tipo);
		
		/*
		 * Ingresa el elemento en el nodo si entra. Sino devuelve false.
		 * Si primero = true entonces pone bad ref en las ref izq y der, sino pone bad ref solo
		 * en la derecha.
		 */
		bool ingresar_interno(TipoDato* clave, TipoDato* dato, bool primero);
		
		/*
		 * Mitad: Mueve el puntero a la pos del elemento mas cercano al medio 
		 * aritmetico del nodo.
		 */
		void mitad();
			 
		
};

#endif
